/*  
 * File:   Frame.h
 * Author: Saulio
 * Created on April 11, 2012, 10:26 PM
 */

#ifndef FRAME_H
#define FRAME_H

#include <stdio.h>
#include <cstdlib>
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;

class Header;
class Investment;
class Guard;
class End_guard;
class Missing_report;
class Adoption_papers;
class Packer;

typedef struct header_type{ 
        char           version;
        char           domain;
        char           type_message;
        unsigned int   id_server_source;
        unsigned int   id_server_destination;
        char            flag;
        unsigned short offset;
}header_prototype;
    
typedef struct id_client_type{
        unsigned short family;
        unsigned short source_port;
        unsigned short destination_port;
        std::string    ip_address_client;  
}id_client_prototype;
    
typedef struct date_time_type{
        char          year[5];
        char          month[3];
        char          day[3];
        char          hour[3];
        char          minutes[3];
        char          seconds[3];
        char          nanoseconds[11];
}date_time_prototype;
    
typedef struct guard_type{
        unsigned int  sequence_number;
        unsigned int  akcnowledge_number;
}guard_prototype;
    
typedef struct end_guard_type{
        unsigned int  sequence_number;
        unsigned int  akcnowledge_number;
}end_guard_prototype;
   
typedef struct missing_report_type{
        unsigned int   sequence_number;
        unsigned int   akcnowledge_number;
        unsigned short tcp_payload;
}missing_report_prototype;
    
typedef struct ip_server_guardian_type{
        unsigned short family;
        std::string    ip_address_guardian;  
}ip_server_guardian_prototype;
     
typedef struct adoption_papers_type{
        unsigned int id_server_guardian;
        ip_server_guardian_prototype ip; 
}adoption_papers_prototype; 

typedef struct message{
        header_prototype                header;
        id_client_prototype             id_client;
        date_time_prototype             date_time;
        guard_prototype                 guard;
        end_guard_prototype             end_guard;
        missing_report_prototype        missing_report;
        ip_server_guardian_prototype    ip_server_guardian;
        adoption_papers_prototype       adoption_papers;
        string padding;
};
 
       
class Header{

public: 
    Header(){}
    Header(char&, char&, unsigned int&, unsigned int&);
    Header(char&, char&, unsigned int&, unsigned int&, 
           unsigned short&, unsigned short&, unsigned short&, char*);  
    Header(const Header& orig);
    virtual ~Header(){}
    
    void Print_header(Header&);
    void Get_time_message(Header&);
    int  System_interface(char*,std::string, int);
    
public:
    
    static const unsigned char  NULL_MESSAGE_TYPE=0xAA;
    static const unsigned short NO_VALID_OFFSET=0xFFFF;
    static const char           NO_FLAG=0;                
    
    header_type header;
    id_client_type id_client;
    date_time_type date_time;
    
    friend class Investment;
    friend class Guard;
    friend class End_guard;
    friend class Missing_report;
    friend class Adoption_papers;
    friend class Packer;
};

void Header::Print_header(Header &tmp)
{
    cout << "La version es: " << int(tmp.header.version) << endl;
    cout << "La tipo de mensaje es: " <<int(tmp.header.type_message) << endl;
    cout << "El dominio es: " << int(tmp.header.domain) << endl;
    cout << "El id del servidor fuente es: " << tmp.header.id_server_source << endl;
    cout << "El id del servidor destino es: " << tmp.header.id_server_destination << endl;
    cout << "El flag esta marcado con: " << int(tmp.header.flag) << endl;
    cout << "El offset es " << tmp.header.offset << endl;    
}

int Header::System_interface(char *ref,std::string c, int n_bytes)
{
    FILE *in;
    char *command=new char[c.size()+1];
    command[c.size()]=0;
    memcpy(command,c.c_str(),c.size());
    in = popen(command ,"r");
    fgets(ref,n_bytes,in);
    pclose(in);
  //  delete[] command, in;
    return (EXIT_SUCCESS);
}

void Header::Get_time_message(Header &h)
{
    System_interface(h.date_time.nanoseconds,"date +%N", sizeof(h.date_time.nanoseconds));
    System_interface(h.date_time.seconds,"date +%S", sizeof(h.date_time.seconds));
    System_interface(h.date_time.minutes,"date +%M", sizeof(h.date_time.minutes));
    System_interface(h.date_time.hour,"date +%k", sizeof(h.date_time.hour));
    System_interface(h.date_time.day,"date +%d", sizeof(h.date_time.day));
    System_interface(h.date_time.month,"date +%m", sizeof(h.date_time.month));
    System_interface(h.date_time.year,"date +%G",sizeof(h.date_time.year));   

    cout << "Nanosegundos: " << h.date_time.nanoseconds;
    cout << "segundos: " << h.date_time.seconds << endl;
    cout << "minutos: " << h.date_time.minutes << endl;
    cout << "horas: " << h.date_time.hour << endl;
    cout << "dias: " << h.date_time.day << endl;
    cout << "meses: " << h.date_time.month << endl;
    cout << "anos: " << h.date_time.year << endl;
}

Header::Header(char &version, char &domain, unsigned int &id_server_source, 
               unsigned int &id_server_destination)
{
    header.version=                 version;
    header.type_message=            NULL_MESSAGE_TYPE;
    header.domain=                  domain;
    header.id_server_source=        id_server_source;
    header.id_server_destination=   id_server_destination;
    header.flag=                    NO_FLAG;
    header.offset=                  NO_VALID_OFFSET;
    
}
Header::Header(char &version,char &domain, unsigned int &id_server_source,
               unsigned int &id_server_destination, unsigned short &family, 
               unsigned short &port_source, unsigned short &port_destination,
               char* ip_address_client)
 {
     Header h(version, domain, id_server_source, id_server_destination);
     h.id_client.family=            family;
     h.id_client.source_port=       port_source;
     h.id_client.destination_port=  port_destination;
     h.id_client.ip_address_client= std::string(ip_address_client);
     Header tmp(h);
     
     cout << "La familia de ip es: " << h.id_client.family << endl;
     cout << "El puerto fuente del cliente es " << h.id_client.source_port << endl;
     cout << "El puerto de destino del cliente es: " << h.id_client.destination_port << endl;
     cout << "La ip del cliente es " << h.id_client.ip_address_client << endl;
     
   //  delete[] ip_address_client;
 }

Header::Header(const Header& orig)
{
    header=orig.header;
    id_client=orig.id_client;
    date_time=orig.date_time;
}




class Investment : public Header  {    

public:
    date_time_type Get_flush_time();
    Investment(Header &investment)
    {
        investment.header.type_message=INVESTMENT_ID;
        investment.header.offset=INVESTMENT_OFFSET;
        investment.header.flag=NO_FLAG;
        investment.date_time=Get_flush_time();
        
        Header tmp(investment);
        
        Print_header(tmp);
    }
    virtual ~Investment(){}

private:
    
    static const char INVESTMENT_ID=0x00;
    static const unsigned short INVESTMENT_OFFSET=sizeof(date_time_type);
    
    friend class Packer;
};

date_time_type Investment::Get_flush_time(){
    
    date_time_type flush_time;
    strcpy(flush_time.year,"0000");
    strcpy(flush_time.month,"00");
    strcpy(flush_time.day,"00");
    strcpy(flush_time.hour,"00");
    strcpy(flush_time.minutes,"00");
    strcpy(flush_time.seconds,"03");
    strcpy(flush_time.nanoseconds,"0000000000");
    return(flush_time);
}



class Guard : public Header {

public:
    Guard(Header &h, unsigned int &s_n, unsigned int &a_n)
    {
        h.header.type_message=GUARD_ID;
        h.header.offset=GUARD_OFFSET;
        h.header.flag=NO_FLAG; 
        h.Get_time_message(h);
        Header tmp(h);
        guard.sequence_number=s_n;
        guard.akcnowledge_number=a_n; 
        
        Print_header(tmp);
        cout <<"el acknowledge es: " << guard.akcnowledge_number << endl;
        cout <<"el numero de sequencia es: " << guard.sequence_number << endl;
  
    }
    virtual ~Guard(){}    

private:
    
    guard_type guard;
    static const char GUARD_ID=0xF0;
    static const unsigned short GUARD_OFFSET=sizeof(guard);
    
    friend class Packer;
};






class End_guard : public Header {

public:
    End_guard(Header &h, unsigned int &s_n,unsigned int &a_n) 
    {
        h.header.type_message=ENDGUARD_ID;
        h.header.offset=END_GUARD_OFFSET;
        h.header.flag=NO_FLAG;
        h.Get_time_message(h);
        Header tmp(h);
        end_guard.sequence_number=s_n;
        end_guard.akcnowledge_number=a_n;
        
        Print_header(tmp);
        cout <<"el acknowledge es: " << end_guard.akcnowledge_number << endl;
        cout <<"el numero de sequencia es: " << end_guard.sequence_number << endl;
    }
    virtual ~End_guard(){}
    
private:
    
    end_guard_type end_guard;
    static const char ENDGUARD_ID=0x0F;
    static const unsigned short END_GUARD_OFFSET=sizeof(end_guard);
    
    friend class Packer;
};






class Missing_report : public Header  {
    
public:
    Missing_report(Header &h, unsigned int &s_n, unsigned int &a_n, 
                   unsigned short &tcp_p)
    {
        h.header.type_message=MISSING_REPORT_ID;
        h.header.offset=MISSING_REPORT_OFFSET;
        h.header.flag=NO_FLAG;
        h.Get_time_message(h);
        Header tmp(h);
        missing_report.sequence_number=s_n;
        missing_report.akcnowledge_number=a_n;
        missing_report.tcp_payload=tcp_p;  
        
        Print_header(tmp);        
        cout <<"el acknowledge es: " << missing_report.akcnowledge_number << endl;
        cout <<"el numero de sequencia es: " << missing_report.sequence_number << endl;
        cout <<"el tcp_payload es : " << missing_report.tcp_payload << endl;
    }
    virtual ~Missing_report(){}
 
private:
    
    missing_report_type missing_report;
    static const char MISSING_REPORT_ID=0xFF;
    static const unsigned short MISSING_REPORT_OFFSET=sizeof(missing_report);
   
    friend class Packer;
};






class Adoption_papers : public Header {

public:
    Adoption_papers(Header &h, unsigned int &id_server_guardian, 
                    char* ip_address_guardian, unsigned short &family)
    {
        h.header.type_message=ADOPTION_PAPERS_ID;
        h.header.offset=ADOPTION_PAPERS_OFFSET;
        h.header.flag=NO_FLAG;
        h.Get_time_message(h);
        Header tmp(h);
        adoption_papers.id_server_guardian=id_server_guardian;
        adoption_papers.ip.family=family;
        adoption_papers.ip.ip_address_guardian=ip_address_guardian;
        
        Print_header(tmp);
        cout <<"el id del servidor guardian : " << adoption_papers.id_server_guardian << endl;
        cout <<"La familia ip: " << adoption_papers.ip.family << endl;
        cout <<"La ip del servidor guardian : " << adoption_papers.ip.ip_address_guardian << endl;                                        
    }
    virtual ~Adoption_papers(){}
    
private:
    adoption_papers_type adoption_papers;       
    static const char ADOPTION_PAPERS_ID=0x8F;
    static const unsigned short ADOPTION_PAPERS_OFFSET=sizeof(adoption_papers);
    
    friend class Packer;
};




class Packer{
    
public:
    Packer(){}
    Packer(Investment &invt, char* ip_address_to)
    { 
       
       package.header=invt.header;
       package.date_time=invt.date_time;
       Send_message(package, ip_address_to); 
    }
    /*Packer(Guard g, std::string ip)
    {
        int r,l;
        r=sizeof(g.header);
        l=sizeof(g.guard);
        Send_message(g.header,g.guard,3000,ip,r,l); 
    }
    Packer(End_guard eg, std::string ip)
    { int r,l;
        r=sizeof(eg.header);
        l=sizeof(eg.end_guard);
        Send_message(eg.header,eg.end_guard,3000,ip,r,l); }
    Packer(Missing_report mr, std::string ip)
    {int r,l;
        r=sizeof(mr.header);
        l=sizeof(mr.missing_report);
        Send_message(mr.header,mr.missing_report,3000,ip,r,l);
     
    }
    Packer(Adoption_papers ap,std::string  ip )
    {int r,l;
        r=sizeof(ap.header);
        l=sizeof(ap.adoption_papers);
        Send_message(ap.header,ap.adoption_papers,3000,ip,r,l);
     }
 */
public:
   
   void Send_message(message&, char*);

   static const unsigned short PO_BOX=710;
   message package;
};

void Packer::Send_message(message &pack, char* ip_char_array)
{  
     
    int sockfd, w_n_bytes;
    struct sockaddr_in serv_addr;
    
    struct iovec v[3];
    
    sockfd=socket(AF_INET, SOCK_STREAM, 0);
    
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=inet_addr(ip_char_array);
    serv_addr.sin_port=htons(PO_BOX); 
    connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)); 
    v[0].iov_base= &pack.header;
    v[0].iov_base=sizeof(pack.header);
    
   /* switch (pack.header.type_message){
   
    case 0x00:
    */
        v[1].iov_base=&pack.date_time;
        v[1].iov_len= sizeof(pack.date_time);
   /* }
    case 0xF0:
    {v[1].iov_base=&pack.guard;
     v[1].iov_len=sizeof(pack.guard);
     v[2].iov_base=&pack.id_client;
     v[2].iov_len=sizeof(pack.id_client);
     v[3].iov_base=&pack.date_time;
     v[3].iov_len= sizeof(pack.date_time);
    }
    case 0x0F:
    { v[1].iov_base=&pack.end_guard;
      v[1].iov_len=sizeof(pack.end_guard);
      v[2].iov_base=&pack.id_client;
     v[2].iov_len=sizeof(pack.id_client);
     v[3].iov_base=&pack.date_time;
     v[3].iov_len= sizeof(pack.date_time);
        
    }
    
    case 0xFF:
    {v[1].iov_base= &pack.missing_report;
    v[1].iov_len= sizeof(pack.missing_report);
    v[2].iov_base=&pack.id_client;
    v[2].iov_len=sizeof(pack.id_client);
    v[3].iov_base=&pack.date_time;
    v[3].iov_len= sizeof(pack.date_time);
    v[4].iov_base=
    }
    case 0x8F:
    {v[1].iov_base=&pack.adoption_papers;
    v[1].iov_len=sizeof(pack.adoption_papers);
    
    }
    
    
    
    
    
    
    }*/
    w_n_bytes= writev(sockfd,v,3);
    close(sockfd);
};

#endif	/* Frame*/